C++

您所在的位置:网站首页 头文件using namespace std C++

C++

2023-03-11 08:00| 来源: 网络整理| 查看: 265

在这里插入图片描述

在这里插入图片描述

文章目录 一、前言二、命名冲突三、命名空间1、域作用限定符2、命名空间的概念👉示例1👉示例2 3、命名空间的定义4、命名空间的使用① 指定命名空间访问【做项目】② 使用using部分展开【做项目】③ 使用using namespace全局展开【日常练习】 5、小结 解答:为何使用using namespace std💡

一、前言

相信大部分在学校学习过C++的同学你们的老师一定会和你们说:现在要写C++的代码了,要换一下头文件用#include ,后面还要带上一个using namespace std;对于前一个头文件的包含和C语言中一样,若是需要使用对应库函数的话就要包一下这个头文件,但是你真的清楚后者吗?

今天我们就来聊聊有关C++中命名空间相关内容,带你好好见识一下什么叫做【命名空间】,并且带着using namespace std;到底是什么的问题好好地探索一番🔍

二、命名冲突

有关命名空间的说法,我们要先从命名冲突讲起

若是我现在写了这么一段C语言代码,在main函数外定义了一个全局变量rand,然后在main函数中使用它,此时会发生什么呢? #include #include int rand = 0; int main(void) { printf("rand = %d\n", rand); return 0; } 运行起来我们可以发现出错了,这是为何呢?我也没有在什么地方定义过这个rand变量呀?其实它并不是和某个rand变量冲突了,而是库中的rand()函数发生了冲突

在这里插入图片描述

通过查看cplusplus可以发现这个函数是包含在头文件中的,而我们上面刚好包含了这个头文件,在程序环境和预处理章节,有提到过对于头文件而言是会在预编译(预处理)阶段展开的,所以便会展开对rand()这个函数的定义,因而和我们定义的rand变量发生了冲突

在这里插入图片描述

我们定义的变量不仅是会和库中的内容发生冲突,若是在公司中工作的时候还会发生和公司中其他同事之间发生冲突假设你的这个项目组写了一段代码上传远端仓库了,另一个项目组是和你们进行对接的,他们也将代码写完进行了上传,此时就有可能会发生定义的冲突

那要怎么去解决这个冲突呢?此时就可以使用到【namespace】命名空间了

三、命名空间 命名空间(namespace)是C++语言特别重要的特性,当第三方供应商提供的库时,为了避免与其他供应商或者用户定义的名字相冲突(命名空间污染),常常将库的内容放置在自己独立的命名空间中。C++标准库也定义了相应命名空间std,用户在使用标准库时必须通过作用域运算符::,或者使用using关键词来简化命名空间中名字的使用 1、域作用限定符

在介绍namespace命名空间之前,我们先来看一个东西叫做【域作用限定符】

首先你要清楚域这个概念,域分为局部域和全局域,对于域来说我们要关注的两点就是 域内变量的使用域的生命周期

我通过下面这个代码来向你演示💻

int a = 0; void f1() { int a = 1; printf("a = %d\n", a); } void f2() { int a = 2; printf("a = %d\n", a); } int main(void) { f1(); return 0; } 对于不同作用域的变量a来说,它所能使用的范围和生命周期都是不同的。对于在全局定义的变量a,它所使用的范围是全局,而对于两个函数f1和f2来说,它们所使用的范围则值局限于当前函数内对于生命周期也是一样,当一个局部变量出了其函数的作用域之外,当前函数的函数栈帧就销毁了,那么外界就无法访问到这个变量了通过观察,可以发现打印出来这个变量a为1,而不是0,这是为什么呢?因为它会优先寻找局部域中的a,而不是去全局找,若是局部没有的话再去全局找

在这里插入图片描述

但我这个时候要强行去访问一下全局的变量a呢,有什么办法吗?那此时就可以使用到【域作用限定符】了

在这里插入图片描述

可以看到,通过在变量a前面加上一个::,这个就叫做域作用限定符,因为这个符号的左边是空的,代表的就是全局域,所以会直接到全局去寻找这个变量a,此时打印出来的就不是这个1了,而不是0,这也就做到了访问当前局部作用域外的内容 2、命名空间的概念

有了域的概念之后,我们再去学习命名空间就好多了,一起来看看吧👀

在C/C++中,变量、函数和后面要学到的类都是大量存在的,这些变量、函数和类的名称将都存在于全局作用域中,可能会导致很多冲突。使用命名空间的目的是对标识符的名称进行本地化,以避免命名冲突或名字污染,namespace关键字的出现就是针对这种问题的

👉示例1

首先我们来看一个示例,也就是上面的那个rand变量的冲突

定义命名空间很简单,只需要在namespace后面跟上命名空间的名字,然后接一对{}即可,{}中即为命名空间的成员。此时我们就可以将rand变量放到这个为 { r } 的命名空间中,那么它就不会和全局域中的其他内容发生冲突了,也就相当于是在荒野中盖了一间小屋子🏠,然后将一只羊🐏放在这个屋子中,狼🐺就很难轻易地发现它 namespace r { int rand = 0; } 那此时既然这个rand变量被保护起来了,我们还能访问到它吗?(可以使用域作用限定符,后面说💬)此时若是我直接去打印这个rand的话出来的结果就不是0了,而是一个随机数,此时它使用的其实就是库中的rand()

在这里插入图片描述

不过有同学会疑惑函数不是要加上()吗,为何这里的rand就可以直接调用这个函数了呢❓

在这里插入图片描述

起初我也很疑惑,不过当我看到了编译器给出的Warning时,就知道了为什么了。若是我们直接使用了rand()这个函数的函数名rand,就相当于是获取到了这个函数的地址对于int(__cdecl *)(void)而言其实是一个函数指针,它指向了这个函数的地址,又刚好因为这个函数我们不需要传入参数,void表示无参,所以也相当于是调用了这个库函数【__cdecl是VS函数名修饰规则,我在函数重载会讲到】

在这里插入图片描述

👉示例2

同样,我们再通过一个示例来了解一下命名空间的概念

我这里定义了两个.h的头文件,里面分别有队列和单链表的结构体,不过对于它们来说我都将结点的结构体名称定义成了Node,此时我若是在.cpp的源文件中同时包含了这两个头文件就会出现问题❌

在这里插入图片描述

#include "Queue.h" #include "List.h" int main(void) { struct Node* qu; struct Node* l; return 0; } 通过运行代码可以看到,编译器报出了【类型重定义】的错误

在这里插入图片描述

那这个时候应该怎么办呢?此时【命名空间】它又可以派上用场了👈 namespace Queue { //队列结构体... } namespace SingleList { //单链表结构体... } 可以看到,若是将它们都封装在各自的【命名空间】里,就不会出现重定义的现象了,那要如何使用各自命名空间中的内容呢?这个我们后面说

在这里插入图片描述

3、命名空间的定义

既然说了这是一个命名空间,那么在一个空间中就可以有很多的内容,而不仅限于一个普通的变量

变量/函数/结构体… 可以看到,除了普通的变量之外,还可以在其内部定义函数、结构体等等 namespace r { int rand = 0; int GetMax(int a, int b) //函数 { return a > b ? a : b; } struct Node { //结构体 struct Node* next; int val; }; } 命名空间嵌套定义 不仅如此,在命名空间内还可以再嵌套定义命名空间 namespace r { int rand = 0; int GetMax(int a, int b) { return a > b ? a : b; } struct Node { struct Node* next; int val; }; namespace r2 { int c; int d; int Sub(int left, int right) { return left - right; } } } 命名空间合并 既然我可以定义命名空间,那别人也可以定义,而且还可能会一模一样。若是我将上面提到过的队列结构体和单链表结构体所使用的结构体所存在的命名空间的名称设置为一样,那在同时包含List.h和Queue.h的时候会出现什么情况呢?当有一个test.cpp中包含了这两个头文件的话两个命名空间中的内容会自动进行一个合并,也就是node这个命名空间中会有两个结构体,但是这个无法调试观察,所以不做过多解释

在这里插入图片描述

4、命名空间的使用

知道了命名空间该如何去定义,现在我们来说说如何去访问定义出来的命名空间的内容

① 指定命名空间访问【做项目】

【使用格式】:命名空间名称::成员

一样是队列和链表的结构,现在它们的结构体名称一样,通过上面的学习可以知道这会发生冲突,于是我将它们分别放入了各自的命名空间中 namespace Queue { struct Node { struct Node* next; int val; }; //... } namespace SList { struct Node { struct Node* next; int val; }; //... } 可以看到,如果是这样的话就访问不到这两个结构体了,也无法定义结构体成员

在这里插入图片描述

此时就需要通过指定命名空间去进行访问Queue::Node和SList::Node,也就需要使用到上面所讲的【域作用限定符 :: 】

在这里插入图片描述 ⚠切记,不要像下面这么去用,因为是Node冲突了,而不是struct这个关键字冲突了

Queue::struct Node qu; SList::struct Node sl; 同理,若是要去访问命名空间内部的其他函数和普通变量的话,也可以这么去用 Queue::InitQueue(&qu); Queue::Push(&qu, 1); Queue::max = 200; SList::PushBack(&sl, 1); SList::PopBack(&sl); SList::max = 300;

但是你不觉得这样很繁琐吗?对于一个队列或者是链表来说,初始化可能就使用到一次,但是入队和尾插就会被频繁使用到,此时我们就可以使用到using关键字来进行一个部分展开了

② 使用using部分展开【做项目】

【使用格式】:using 命名空间名称::成员

我们可以将这个命名空间中常用的成员函数在头部进行using声明

using声明引入的名字的作用域满足一般的作用域规则:有效范围从using声明的地方开始,到其语句所在的作用域结束时为止

using Queue::Push; using SList::PushBack; using SList::PopBack;

在这里插入图片描述

在面对C++中std标准库里的【cout】和【endl】时,我们就需要使用到using namespace std;,因为他们都定义在了标准库中,只是包含头文件是不够的但若是你不想这么去写(这样写并不好),那就只能一个个地指定访问,可以看到这也是非常得繁琐,这要是在一个项目中有几百个输出那不得疯了🤪 #include int main(void) { std::cout


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3